#ifndef __MCS__TRUSTED_CAST__HPP
#define __MCS__TRUSTED_CAST__HPP

#include "Object.hpp"
#include "RunTimeException.hpp"
#include "NullPointerException.hpp"

namespace detail
{
	template<class T> class trusted_cast_tag
	{
	public:
		trusted_cast_tag(char const * file, int line, char const * code, T const & val)
			: value_(val)
			, location_(file, line)
			, expression_(code)
		{}

		SourceLocation location() const { return location_; }
		String expression() const { return expression_; }
		T value() const { return value_; }
	private:
		SourceLocation location_;
		String expression_;
		T value_;
	};
}

template<class Y> detail::trusted_cast_tag<Y*> make_trusted_cast(char const * file, int line, char const * expr, Y * p)
{	return detail::trusted_cast_tag<Y*>(file, line, expr, p); }
template<class Y> detail::trusted_cast_tag< ptr<Y> > make_trusted_cast(char const * file, int line, char const * expr, ptr<Y> const & p)
{	return detail::trusted_cast_tag< ptr<Y> >(file, line, expr, p); }

#define trusted_cast(x) make_trusted_cast(__FILE__, __LINE__, #x, (x))

class TrustedCastFailureException : public RunTimeException
{
public:
	TrustedCastFailureException(SourceLocation const & location, String const & expr, char const * srcType, char const * dstType)
		: RunTimeException()
		, location_(location)
		, expression_(expr)
		, srcType_(srcType)
		, dstType_(dstType)
	{

	}

	SourceLocation location() const { return location_; }
	String expression() const { return expression_; }
	String sourceTypeName() const { return srcType_; }
	String destinationTypeName() const { return dstType_; }

	virtual std::ostream & stream(std::ostream & str) const
	{
		return str << location_ << ": Trusted cast failed: expression \"" << expression_ << "\" formed pointer of type \"" << srcType_ << "\", target type is \"" << dstType_ << "\".";
	}
private:
	SourceLocation location_;
	String expression_;
	String srcType_;
	String dstType_;
};

template<class T> template<class Y> inline ptr<T>::ptr(detail::trusted_cast_tag<Y*> const & tc)
: pObj_(dynamic_cast<T*>(tc.value()))
{
	if(pObj_)
		pObj_->retain();
	else
	{
		throw TrustedCastFailureException(tc.location(), typeid(*tc.value().get()).name(), typeid(T).name());
	}
}

template<class T> template<class Y> inline ptr<T>::ptr(detail::trusted_cast_tag< ptr<Y> > const & tc)
: pObj_(dynamic_cast<T*>(tc.value().get()))
{
	if(pObj_)
		pObj_->retain();
	else
	{
		if(!tc.value())
		{
			throw NullPointerException(tc.location(), tc.expression());
		}
		else
		{
			TrustedCastFailureException ex(
				tc.location(), tc.expression(),
				typeid(*tc.value().get()).name(), typeid(T).name()
			);
			throw ex;
		}
	}
}

#endif //__MCS__TRUSTED_CAST__HPP
